package com.gitee.cashzhang27.test.boot.activiti.controller;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import lombok.AllArgsConstructor;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.Model;
import org.activiti.explorer.util.XmlUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

/**
 * 流程模型Model操作相关
 *
 * @author Cash Zhang
 * @version v1.0
 * @since 2019/04/12 11:34
 */
@RestController
@AllArgsConstructor
@RequestMapping("models")
@Api(value = "ModelController", description = "流程模型模块")
public class ModelController {

  private ProcessEngine processEngine;
  private ObjectMapper objectMapper;
  private RepositoryService repositoryService;

  /**
   * 新建一个空模型
   */
  @ApiOperation(value = "新建一个空模型")
  @PostMapping
  public Map<String, Object> newModel(String name, String key, String description)
      throws UnsupportedEncodingException {
    RepositoryService repositoryService = processEngine.getRepositoryService();
    //初始化一个空模型
    Model model = repositoryService.newModel();
    int revision = 1;
    ObjectNode modelNode = objectMapper.createObjectNode();
    modelNode.put(ModelDataJsonConstants.MODEL_NAME, name);
    modelNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, description);
    modelNode.put(ModelDataJsonConstants.MODEL_REVISION, revision);

    model.setName(name);
    model.setKey(key);
    model.setMetaInfo(modelNode.toString());

    repositoryService.saveModel(model);
    String id = model.getId();

    //完善ModelEditorSource
    ObjectNode editorNode = objectMapper.createObjectNode();
    editorNode.put("id", "canvas");
    editorNode.put("resourceId", "canvas");
    ObjectNode stencilSetNode = objectMapper.createObjectNode();
    stencilSetNode.put("namespace",
        "http://b3mn.org/stencilset/bpmn2.0#");
    editorNode.put("stencilset", stencilSetNode);
    repositoryService.addModelEditorSource(id, editorNode.toString().getBytes(StandardCharsets.UTF_8));
//        return new ModelAndView("redirect:/modeler.html?modelId=" + id);
    Map<String, Object> data = new HashMap<>();
    data.put("modelId", id);
    return success(data);
  }
  /*
    @ApiOperation(value = "获取所有模型")
    @GetMapping
    public TableResultResponse modelList() {
        RepositoryService repositoryService = processEngine.getRepositoryService();
        List<Model> list = repositoryService.createModelQuery().orderByCreateTime().desc().list();
        for (Model model : list) {
        }
        TableResultResponse response = new TableResultResponse<>(list.size(),list);
        return response;
    }
  */

  /**
   * 删除模型
   */
  @ApiOperation(value = "删除模型")
  @DeleteMapping("{id}")
  public Object deleteModel(@PathVariable("id") String id) {
    RepositoryService repositoryService = processEngine.getRepositoryService();
    repositoryService.deleteModel(id);
    return success();
  }

  /**
   * 发布模型为流程定义
   */
  @ApiOperation(value = "发布模型为流程定义")
  @PostMapping("{id}/deployment")
  public Object deploy(@PathVariable("id") String id) throws Exception {

    //获取模型
    RepositoryService repositoryService = processEngine.getRepositoryService();
    Model modelData = repositoryService.getModel(id);
    byte[] bytes = repositoryService.getModelEditorSource(modelData.getId());

    if (bytes == null) {
      return failed("模型数据为空，请先设计流程并成功保存，再进行发布。");
    }

    JsonNode modelNode = new ObjectMapper().readTree(bytes);

    BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
    if (model.getProcesses().size() == 0) {
      return failed("数据模型不符要求，请至少设计一条主线流程。");
    }
    byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);

    //发布流程
    String processName = modelData.getName() + ".bpmn20.xml";
    DeploymentBuilder deploymentBuilder = repositoryService.createDeployment()
        .name(modelData.getName())
        .addString(processName, new String(bpmnBytes, StandardCharsets.UTF_8));

    Deployment deployment = deploymentBuilder.deploy();
    modelData.setDeploymentId(deployment.getId());
    repositoryService.saveModel(modelData);

    return success();
  }

  @ApiOperation(value = "上传一个已有模型")
  @RequestMapping(value = "/uploadFile", method = RequestMethod.POST)
  public void deployUploadedFile(
      @RequestParam("uploadfile") MultipartFile uploadfile) {
    InputStreamReader in = null;
    try {
      try {
        boolean validFile = false;
        String fileName = uploadfile.getOriginalFilename();
        if (fileName != null && (fileName.endsWith(".bpmn20.xml") || fileName.endsWith(".bpmn"))) {
          validFile = true;

          XMLInputFactory xif = XmlUtil.createSafeXmlInputFactory();
          in = new InputStreamReader(new ByteArrayInputStream(uploadfile.getBytes()), "UTF-8");
          XMLStreamReader xtr = xif.createXMLStreamReader(in);
          BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);

          if (bpmnModel.getMainProcess() == null || bpmnModel.getMainProcess().getId() == null) {
//                        notificationManager.showErrorNotification(Messages.MODEL_IMPORT_FAILED,
//                                i18nManager.getMessage(Messages.MODEL_IMPORT_INVALID_BPMN_EXPLANATION));
            System.out.println("err1");
          } else {

            if (bpmnModel.getLocationMap().isEmpty()) {
//                            notificationManager.showErrorNotification(Messages.MODEL_IMPORT_INVALID_BPMNDI,
//                                    i18nManager.getMessage(Messages.MODEL_IMPORT_INVALID_BPMNDI_EXPLANATION));
              System.out.println("err2");
            } else {

              String processName = null;
              if (StringUtils.isNotEmpty(bpmnModel.getMainProcess().getName())) {
                processName = bpmnModel.getMainProcess().getName();
              } else {
                processName = bpmnModel.getMainProcess().getId();
              }
              Model modelData;
              modelData = repositoryService.newModel();
              ObjectNode modelObjectNode = new ObjectMapper().createObjectNode();
              modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, processName);
              modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
              modelData.setMetaInfo(modelObjectNode.toString());
              modelData.setName(processName);

              repositoryService.saveModel(modelData);

              BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
              ObjectNode editorNode = jsonConverter.convertToJson(bpmnModel);

              repositoryService
                  .addModelEditorSource(modelData.getId(), editorNode.toString().getBytes("utf-8"));
            }
          }
        } else {
//                    notificationManager.showErrorNotification(Messages.MODEL_IMPORT_INVALID_FILE,
//                            i18nManager.getMessage(Messages.MODEL_IMPORT_INVALID_FILE_EXPLANATION));
          System.out.println("err3");
        }
      } catch (Exception e) {
        String errorMsg = e.getMessage().replace(System.getProperty("line.separator"), "<br/>");
//                notificationManager.showErrorNotification(Messages.MODEL_IMPORT_FAILED, errorMsg);
        System.out.println("err4");
      }
    } finally {
      if (in != null) {
        try {
          in.close();
        } catch (IOException e) {
//                    notificationManager.showErrorNotification("Server-side error", e.getMessage());
          System.out.println("err5");
        }
      }
    }
  }

  private Map<String, Object> success(Map<String, Object> data) {
    Map<String, Object> map = new HashMap<>(data);
    map.put("status", true);
    map.put("reason", "操作成功");
    return map;
  }

  private Map<String, Object> success() {
    Map<String, Object> map = new HashMap<>(2);
    map.put("status", true);
    map.put("reason", "操作成功");
    return map;
  }

  private Map<String, Object> failed(String reason) {
    Map<String, Object> map = new HashMap<>(2);
    map.put("status", false);
    map.put("reason", "操作失败：" + reason);
    return map;
  }

}
